﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.Text;

namespace NMemcached.Client.Configuration
{
    internal class HashAlgorithmConverter : ConfigurationConverterBase
    {
        public HashAlgorithmConverter()
            : base()
        {
        }

        public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType)
        {
            bool canConvert = base.CanConvertFrom(context, sourceType);

            if (sourceType.IsAssignableFrom(typeof(string)))
            {
                canConvert = true;
            }

            return canConvert;
        }

        public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType)
        {
            bool canConvert = base.CanConvertTo(context, destinationType);

            if (destinationType.IsAssignableFrom(typeof(string)))
            {
                canConvert = true;
            }

            return canConvert;
        }

        public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
        {
            object converted = null;

            if (value.GetType() == typeof(string))
            {
                if (value != null)
                {
                    string algorithmString = value.ToString().ToLower();

                    switch (algorithmString.ToLower())
                    {
                        case "fnv32":
                            converted = new FNVManaged(FNVManaged.FNVAlgorithm.FNV1, false);
                            break;
                        case "fnv64":
                            converted = new FNVManaged(FNVManaged.FNVAlgorithm.FNV1, true);
                            break;
                        case "fnv1a64":
                            converted = new FNVManaged(FNVManaged.FNVAlgorithm.FNV1a, true);
                            break;
                        case "md5":
                            converted = new MD5CryptoServiceProvider();
                            break;
                        case "sha1":
                            converted = new SHA1Managed();
                            break;
                        case "sha256":
                            converted = new SHA256Managed();
                            break;
                        case "sha384":
                            converted = new SHA384Managed();
                            break;
                        case "sha512":
                            converted = new SHA512Managed();
                            break;
                        case "fnv1a32":
                        default:
                            converted = new FNVManaged(FNVManaged.FNVAlgorithm.FNV1a, false);
                            break;
                    }
                }
            }
            else
            {
                converted = base.ConvertFrom(context, culture, value);
            }

            return converted;
        }

        public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType)
        {
            object converted = null;

            Type valueType = value.GetType();

            StringBuilder buffer = new StringBuilder();

            if (valueType.IsAssignableFrom(typeof(HashAlgorithm)) && destinationType.IsAssignableFrom(typeof(string)))
            {
                if (valueType.IsAssignableFrom(typeof(FNVManaged)))
                {
                    FNVManaged fnv = (FNVManaged)value;
                    buffer.Append(fnv.Algorithm.ToString().ToLower());
                    buffer.Append(fnv.HashSize == 64 ? "64" : "32");
                }

                if (valueType.IsAssignableFrom(typeof(MD5)))
                {
                    buffer.Append("md5");
                }

                if (valueType.IsAssignableFrom(typeof(SHA1)))
                {
                    buffer.Append("sha1");
                }

                if (valueType.IsAssignableFrom(typeof(SHA256)))
                {
                    buffer.Append("sha256");
                }

                if (valueType.IsAssignableFrom(typeof(SHA384)))
                {
                    buffer.Append("sha384");
                }

                if (valueType.IsAssignableFrom(typeof(SHA512)))
                {
                    buffer.Append("sha512");
                }

                converted = buffer.ToString();
            }
            else
            {
                converted = base.ConvertTo(context, culture, value, destinationType);
            }

            return converted;
        }
    }
}
